package com.taint.utils;


import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.dynamic.DynamicType;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Utils {
    public static final String CLASS_FILE_REGEX = "[a-zA-Z_.$]+[a-zA-Z_1-9.$]*.class";

    public static boolean isEmpty(Object str){
        if(str !=null){
            return true;
        }
        return false;
    }


    public static String escapeHtml(String text) {
        return text.
                replace("&", "&amp;").
                replace("<", "&lt;").
                replace(">", "&gt;").
                replace("\"", "&quot;");
    }

    public static void OutPutClazz(DynamicType.Builder<?> builder, String className) {
        byte[] bytes = builder.make().getBytes();
        if (bytes.length > 0) {
            try {
                Utils.OutPutClazz(bytes, className);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    public static File OutPutClazz(byte[] bytes, String clazzName) throws IOException {
        FileOutputStream out = null;

        File file = new File("/tmp/agent/debug/" + clazzName + ".class");
        if (file.exists()) {
            file.delete();
        }
        file.getParentFile().mkdirs();
        try {
            out = new FileOutputStream(file);
            out.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            out.close();

        }

        return file;

    }


    /**
     * com.foo.Bar -> com/foo/Bar.class
     */
    public static String getResourceName(final String className) {
        if (!className.endsWith(".class")) {
            return className.replace('.', '/') + ".class";
        } else {
            return className;
        }
    }


    /*
    rewrite  by  taochao ,origin   sinktank
     */

    public static String toInternalMethodSignature(String returnType, ArrayList<String> parameterTypes) {
        StringBuilder buffer = new StringBuilder();

        if(parameterTypes == null){
            return buffer.append("(").append(")").append(toInternalType(returnType)).toString();
        }
        buffer.append('(');
        for (int i = 0; i < parameterTypes.size(); i++)
            buffer.append(toInternalType(parameterTypes.get(i)));
        buffer.append(')');
        buffer.append(toInternalType(returnType));

        return buffer.toString();
    }

    static final Pattern ARRAY_PATTERN = Pattern.compile("\\[\\]");

    public static String toInternalType(String type) {
        StringBuilder buffer = new StringBuilder();

        Matcher matcher = ARRAY_PATTERN.matcher(type);
        int firstArray = -1;
        int start = 0;
        int length = type.length();
        while (matcher.find(start)) {
            start = matcher.start();
            if (firstArray < 0)
                firstArray = start;

            buffer.append('[');
            start += 2;
            if (start >= length)
                break;
        }

        String componentType = firstArray > 0 ? type.substring(0, firstArray) : type;

        String primitive = null;
        if (componentType.equals("boolean"))
            primitive = "Z";
        else if (componentType.equals("byte"))
            primitive = "B";
        else if (componentType.equals("char"))
            primitive = "C";
        else if (componentType.equals("double"))
            primitive = "D";
        else if (componentType.equals("float"))
            primitive = "F";
        else if (componentType.equals("int"))
            primitive = "I";
        else if (componentType.equals("long"))
            primitive = "J";
        else if (componentType.equals("short"))
            primitive = "S";
        else if (componentType.equals("void"))
            primitive = "V";


        if (primitive == null) {
            buffer.append('L');

            buffer.append(componentType.replace('.', '/'));

            buffer.append(';');
        } else
            buffer.append(primitive);

        return buffer.toString();
    }


    /**
     * 判断文件夹是否存在

     * true 文件不存在，false 文件存在不做任何操作
     */
    public static boolean isExist(String filePath) {
        String paths[] = filePath.split("\\/");
        String dir = paths[0];
        for (int i = 0; i < paths.length - 2; i++) {
            try {
                dir = dir + File.separator + paths[i + 1];
                File dirFile = new File(dir);
                if (!dirFile.exists()) {
                    dirFile.mkdir();
                    //System.out.println("创建目录为：" + dir);
                }
            } catch (Exception err) {
                //System.err.println("ELS - Chart : 文件夹创建发生异常");
            }
        }
        File fp = new File(filePath);
        if(!fp.exists()){
            return true; // 文件不存在，执行下载功能
        }else{
            return false; // 文件存在不做处理
        }
    }


    /**
     * Get the full name of the Java class in a given class file, relative to the given package
     * root directory.
     *
     * @param rootDir the package root directory
     * @param classFile the class file
     * @return the full Java class name
     */
    public static String getClassName(File rootDir, File classFile) {
        return rootDir.toURI()
                .relativize(classFile.toURI())
                .getPath()
                .replace('/', '.')
                .replace(".class", "");
    }


    /**
     * Get a list of all Java class files in a given directory, including its subdirectories.
     *
     * @param targetDir the name of the target directory
     * @return a list of all class files in the directory
     */
    public static List<File> getClassFiles(File targetDir) {
        List<File> classFiles = new ArrayList<>();
        for (File file : targetDir.listFiles()) {
            if (file.isDirectory()) {
                classFiles.addAll(getClassFiles(file));
            }
            if (file.getName().matches(CLASS_FILE_REGEX)) {
                classFiles.add(file);
            }
        }
        return classFiles;
    }

public static void getJarFiles(String JarPath) {
        File dir = new File(JarPath);
        File[] files = dir.listFiles();

        if (files == null)
            return;
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {
                //jarFileList.add(files[i]);
                getJarFiles(files[i].getAbsolutePath());
            } else {
                String strFileName = files[i].getAbsolutePath().toLowerCase();
                // System.out.println("---"+strFileName);
                if(strFileName.endsWith(".jar"))
                jarFileList.add(files[i]);
            }
        }

    }

    public static  List<ClassFile> extractClass(String JarPath){

        getJarFiles(JarPath);
        Set<ClassFile> jarClassFiles=new HashSet<>();
        for(File jarFile:jarFileList){
            //这里判断class是否在我们的要求中
            try {
                JarFile jar=new JarFile(jarFile);
                Enumeration  enumeration=jar.entries();
                while (enumeration.hasMoreElements()){
                    JarEntry jarEntry= (JarEntry) enumeration.nextElement();
                    if (!jarEntry.getName().endsWith(".class")) continue;
                    String replace = jarEntry.getName().replace("/", ".");
                    if(isNeedLoad(replace)){
                        InputStream is=jar.getInputStream(jarEntry);
                        jarClassFiles.add(new ClassFile(replace,is));
                        OutPutClazz(is,replace);
                    }

                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


        return new ArrayList<>(jarClassFiles);
    }

    private static boolean isNeedLoad(String replace) {

        Pattern pattern=Pattern.compile(Configuration.getStartWithRegex());
        Matcher matcher=pattern.matcher(replace);
        if(matcher.find()){
            return true;
        }
        return false;
    }

}
